package com.moyuwanjia.TestProxy.cglib;

import com.moyuwanjia.TestProxy.proxy.IProducer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.jupiter.api.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author 摸鱼丸加~
 * @create 2022-07-11 11:33
 * 模拟一个消费者
 *
 *   测试基于接口的动态代理，至少必须实现一个接口
 */
public class Client {
    /**
     * 动态代理：
     *      特点：字节码随用随创建，随用随加载
     *      作用：不锈钢盖源码的基础上对方法增强
     *      分类：
     *          基于接口的动态代理
     *          基于子类的动态代理
     *      基于接口的动态代理：
     *          涉及的类：Proxy类中的newProxyInstance（JDK官方提供）
     *      创建代理对象的要求：
     *          被代理类最少实现一个接口，如果没有实现则不能使用
     * newProxyInstance()的参数
     *      ClassLoader:类加载器，用于加载代理对象的字节码，与被大力对象使用相同的类加载器，
     *      固定写法（代理的是谁，就写谁的classLoader）
     *      Class[]:字节码数组，它是用于让代理对象和被代理对象有相同方法（固定写法）。
     *      InvocationHandler:用于提供增强的代码，让我们写如何代理，我们一般都是写一个该接口的实现类，
     *      通常情况下都是匿名内部类，但不是必须的。此接口的实现类都是谁用谁写。
     */
    @Test
    public void test01() {
        //当匿名内部类访问外部成员变量时，外部成员要求是最终的，所以使用final修饰
       final Producer producer = new Producer();
        IProducer iProducer  =(IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 作用：执行被代理对象的任何接口方法都会经过该方法
                     * @param proxy 代理对象的引用
                     * @param method 当前执行的方法
                     * @param args 当前执行方法所需的参数
                     * @return 被代理对象方法相同的返回值
                     * @throws Throwable
                     * 模拟代理商对商品抽成百分之20
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //1.提供增强的代码
                        Object returnValue = null;
                        //2.判断当前方法是不是销售方法
                        if ("selectProduct".equals(method.getName())) {
                            returnValue = method.invoke(producer,((float)args[0]*0.8F));
                        }
                        return returnValue;
                    }
                });
            iProducer.selectProduct(10000);
    }
    /**
     * 动态代理：
     *      特点：字节码随用随创建，随用随加载
     *      作用：不修改源码的基础上对方法增强
     *      分类：
     *          基于接口的动态代理
     *          基于子类的动态代理
     *      基于子类的动态代理：
     *          涉及的类：Enhancer(第三方cglib集)
     *      如何创建代理对象：
     *          使用Enhancer类中的create方法
     *      创建代理对象的要求：
     *          被代理类不能是最终类
     * create()方法的参数
     *      Class:字节码，它是用于指定被代理对象的字节码。
     *      Callback:用于提供增强的代码
     *        让我们写如何代理，我们一般都是写一个该接口的实现类，
     *      通常情况下都是匿名内部类，但不是必须的。此接口的实现类都是谁用谁写。
     */
    @Test
    public void test02(){
        //当匿名内部类访问外部成员变量时，外部成员要求是最终的，所以使用final修饰
        final Producer02 producer02 = new Producer02();
        Producer02 producer = (Producer02) Enhancer.create(producer02.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy 代理对象的引用
             * @param method 当前执行的方法
             * @param args 当前执行方法所需的参数
             * @param methodProxy 当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //1.提供增强的代码
                Object returnValue = null;
                //2.判断当前方法是不是销售方法
                if ("selectProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer02, ((float) args[0] * 0.8F));
                }
                return returnValue;
            }
        });
        producer.selectProduct(10000);

    }
}
